package cn.jtfadmin.base.lang.common.utils;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.CompressorOutputStream;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.file.Files;

/**
 * 压缩工具
 * @author jtf
 * @since 0.0.1
 * @version 0.0.1
 */
public class CompressUtils {


    private static void createTargetFileDir(File targetFile){
        if(!targetFile.exists()){
            targetFile.mkdirs();
            return;
        }
        if(!targetFile.isDirectory()){
            throw new IllegalArgumentException("目标文件不是一个目录");
        }
    }


    private static void createTargetFile(File targeFile){
        if(targeFile.exists() && targeFile.isDirectory()){
            throw new IllegalArgumentException("压缩结果文件不能是目录");
        }
        if(!targeFile.exists()){
            targeFile.getParentFile().mkdirs();
            try {
                targeFile.createNewFile();
            } catch (IOException e) {
                throw new IllegalArgumentException("创建文件异常");
            }
        }
    }


    public static void decompressJar(File targeFileDir, File source){

        try(JarArchiveInputStream jarArchiveInputStream = new JarArchiveInputStream(new FileInputStream(source))){
            doDecompress(jarArchiveInputStream, targeFileDir);
        }catch (IOException e){
            throw new RuntimeException(e);
        }

    }

    private static void doDecompress(ArchiveInputStream i, File targeFileDir) throws IOException {
        createTargetFileDir(targeFileDir);
        ArchiveEntry entry = null;
        while ((entry = i.getNextEntry()) != null) {
            if (!i.canReadEntryData(entry)) {
                // 记录一些东西？
            }
            String name = entry.getName();
            File f = new File(targeFileDir, name);
            if (entry.isDirectory()) {
                if (!f.isDirectory() && !f.mkdirs()) {
                    throw new IOException("无法创建目录" + f);
                }
            } else {
                File parent = f.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("无法创建目录" + parent);
                }
                try (OutputStream o = Files.newOutputStream(f.toPath())) {
                    IOUtils.copy(i, o);
                }
            }
        }
    }


    /**
     * 压缩文件到zip
     * @param targetFile 压缩后的目标文件
     * @param source 源文件
     */
    public static void compressZip(File targetFile, File source){
        createTargetFile(targetFile);
        try(ZipArchiveOutputStream outputStream = new ZipArchiveOutputStream(new FileOutputStream(targetFile))){
            doCompress(outputStream, "", source);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    private static void doCompress(ArchiveOutputStream outputStream,  String parentName, File... sources) throws IOException {
        if(StringUtils.hasText(parentName)){
            parentName = parentName + File.separator;
        }
        for (File f : sources) {
            String name = parentName + f.getName();
            // maybe skip directories for formats like AR that don't store directories
            ArchiveEntry entry = outputStream.createArchiveEntry(f, name);
            // potentially add more flags to entry
            outputStream.putArchiveEntry(entry);
            if (f.isFile()) {
                try (InputStream i = Files.newInputStream(f.toPath())) {
                    IOUtils.copy(i, outputStream);
                }
            }
            outputStream.closeArchiveEntry();
            if(f.isDirectory()){
                File[] files = f.listFiles();
                doCompress(outputStream, name, files);
            }
        }

    }


}
